# -*- python -*-
# ex: set syntax=python:

# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# It has one job: define a dictionary named BuildmasterConfig. This
# dictionary has a variety of keys to control different aspects of the
# buildmaster. They are documented in docs/config.xhtml .

from buildbot.changes import filter
from buildbot.config import BuilderConfig
from buildbot.process.factory import BuildFactory
from buildbot.schedulers.basic import SingleBranchScheduler
from buildbot.status.mail import MailNotifier
from buildbot.steps.shell import ShellCommand
from buildbot.steps.source import Repo
from master import slaves_list
from master import master_utils
from master.repo_poller import RepoPoller

import master_site_config
import config

c = BuildmasterConfig = {}


ActiveMaster = master_site_config.ART


# Factory definitions. TODO(ngeoffray): Move this code to recipes.
checkout = Repo(
    manifest_url = 'https://android.googlesource.com/platform/manifest',
    manifest_branch = 'master-art')

full_checkout = Repo(
    manifest_url = 'https://android.googlesource.com/platform/manifest',
    manifest_branch = 'master')

maybe_clobber = ShellCommand(
    name = 'maybe clobber',
    command = ['rm', '-rf', 'out' ],
    description = 'maybe clobber',
    doStepIf=(lambda(step): step.build.getProperties().has_key('clobber')))

def setup_host_x86(factory, name, x86_64=False):
  build_top_dir = '${PWD}/%s/%s' % (name, factory.workdir)
  env = { 'TARGET_PRODUCT': 'sdk',
          'TARGET_BUILD_VARIANT': 'eng',
          'TARGET_BUILD_TYPE': 'release',
          'ANDROID_BUILD_TOP': build_top_dir,
          'PATH': '%s/out/host/linux-x86/bin:${PATH}' % build_top_dir,
          'ART_USE_OPTIMIZING_COMPILER' : 'true',
          'ART_TEST_RUN_TEST_2ND_ARCH': 'false',
          'ART_TEST_INTERPRETER': 'true',
          'ART_TEST_OPTIMIZING': 'true',
          'ART_TEST_FULL': 'false',
          'ART_TEST_KEEP_GOING': 'true' }

  if x86_64:
    bitness = 64
  else:
    bitness = 32
    env.update({ 'HOST_PREFER_32_BIT' : 'true' })

  build = ShellCommand(
      name = 'build sdk-eng',
      command = ['art/tools/buildbot-build.sh', '-j8', '--host'],
      haltOnFailure = True,
      description = 'build sdk-eng',
      env = env)

  test1 = ShellCommand(
      name = 'test cc',
      command = ['make', '-j8', 'test-art-host-gtest%s' % bitness],
      haltOnFailure = False,
      description = 'test gtest',
      timeout = 2400,
      env = env)

  test2 = ShellCommand(
      name = 'test quick',
      command = ['make', '-j8', 'test-art-host-run-test-default', 'dist'],
      haltOnFailure = False,
      description = 'test quick',
      env = env)

  optimizing_env = env.copy()
  optimizing_env.update({ 'ART_TEST_RUN_TEST_DEBUGGABLE': 'true' })
  test3 = ShellCommand(
      name = 'test optimizing',
      command = ['make', '-j8', 'test-art-host-run-test-optimizing', 'dist'],
      haltOnFailure = False,
      description = 'test optimizing',
      env = optimizing_env)

  test4 = ShellCommand(
      name = 'test interpreter',
      # Use a lower -j number for interpreter, some tests take a long time
      # to run on it.
      command = ['make', '-j5', 'test-art-host-run-test-interpreter', 'dist'],
      haltOnFailure = False,
      description = 'test interpreter',
      timeout = 2400,
      env = env)
  
  # We explicitly give a wrong path for the image, to ensure vogar
  # will create a boot image with the default compiler. Note that
  # giving an existing image on host does not work because of
  # classpath/resources differences when compiling the boot image.
  #
  # Also we increase the timeout, as vogar cannot set individual test
  # timeout when being asked to run packages.
  test5 = ShellCommand(
      name = 'test libcore',
      command = ['art/tools/run-libcore-tests.sh',
                 '--mode=host',
                 '--variant=X%s' % bitness,
                 '--vm-arg', '-Ximage:/non/existent'],
      haltOnFailure = False,
      description = 'test libcore',
      env = env)

  test6 = ShellCommand(
      name = 'test jdwp',
      command = ['art/tools/run-jdwp-tests.sh',
                 '--mode=host',
                 '--variant=X%s' % bitness],
      haltOnFailure = False,
      description = 'test jdwp',
      env = env)

  factory.addStep(checkout)
  factory.addStep(maybe_clobber)
  factory.addStep(build)
  factory.addStep(test1)
  factory.addStep(test2)
  factory.addStep(test3)
  factory.addStep(test4)
  factory.addStep(test5)
  factory.addStep(test6)


def setup_target(factory, name, serial, debug, device):
  build_top_dir = '${PWD}/%s/%s' % (name, factory.workdir)
  android_root = '/data/local/tmp/system'

  env = {'TARGET_BUILD_VARIANT': 'eng',
         'TARGET_BUILD_TYPE': 'release',
         'ANDROID_SERIAL': serial,
         'ANDROID_BUILD_TOP': build_top_dir,
         'PATH': '%s/out/host/linux-x86/bin:${PATH}' % build_top_dir,
         'ART_TEST_RUN_TEST_2ND_ARCH': 'false',
         'ART_USE_OPTIMIZING_COMPILER' : 'true',
         'ART_TEST_INTERPRETER': 'true',
         'ART_TEST_OPTIMIZING': 'true',
         'ART_TEST_FULL': 'false',
         'ART_TEST_ANDROID_ROOT': android_root,
         'USE_DEX2OAT_DEBUG': 'false',
         'ART_BUILD_HOST_DEBUG': 'false',
         'ART_TEST_KEEP_GOING': 'true'}

  if not debug:
    env.update({ 'ART_TEST_RUN_TEST_NDEBUG' : 'true' })
    env.update({ 'ART_TEST_RUN_TEST_DEBUG' : 'false' })

  if device == 'volantis':
    bitness = 64
    j_test_option = '-j2'
    env.update({ 'TARGET_PRODUCT': 'armv8' })
    env.update({ 'ANDROID_PRODUCT_OUT':
                     '%s/out/target/product/armv8' % build_top_dir })

  elif device == 'hammerhead':
    bitness = 32
    j_test_option = '-j4'
    env.update({ 'TARGET_PRODUCT': 'arm_krait' })
    env.update({ 'ANDROID_PRODUCT_OUT':
                     '%s/out/target/product/arm_krait' % build_top_dir })

  elif device == 'fugu':
    bitness = 32
    j_test_option = '-j4'
    env.update({ 'TARGET_PRODUCT': 'silvermont' })
    env.update({ 'ANDROID_PRODUCT_OUT':
                     '%s/out/target/product/silvermont' % build_top_dir })

  # We need the -e option to set TARGET_GLOBAL_LDFLAGS.
  build = ShellCommand(
      name = 'build target',
      command = ['art/tools/buildbot-build.sh', '-j8', '--target'],
      haltOnFailure = True,
      description = 'build target',
      env = env)

  sync = ShellCommand(
      name = 'build target',
      command = ['make', 'test-art-target-sync'],
      haltOnFailure = True,
      description = 'sync to device',
      env = env)

  test_env = env.copy()
  test_env.update({ 'ART_TEST_NO_SYNC': 'true' })

  test_gtest = ShellCommand(
      name = 'test gtest',
      command = ['make', j_test_option, 'test-art-target-gtest%s' % bitness],
      haltOnFailure = False,
      description = 'test gtest',
      timeout = 2400,
      env = test_env)

  test_quick = ShellCommand(
      name = 'test quick',
      command = ['make', j_test_option,
                 'test-art-target-run-test-default', 'dist'],
      haltOnFailure = False,
      description = 'test quick',
      env = test_env)

  optimizing_env = test_env.copy()
  optimizing_env.update({ 'ART_TEST_RUN_TEST_DEBUGGABLE': 'true' })
  test_optimizing = ShellCommand(
      name = 'test optimizing',
      command = ['make', '-e', j_test_option,
                 'test-art-target-run-test-optimizing', 'dist'],
      haltOnFailure = False,
      description = 'test optimizing',
      env = optimizing_env)

  test_interpreter = ShellCommand(
      name = 'test interpreter',
      command = ['make', '-e', j_test_option,
                 'test-art-target-run-test-interpreter', 'dist'],
      haltOnFailure = False,
      description = 'test interpreter',
      timeout = 2400,
      env = test_env)

  test_libcore = ShellCommand(
      name = 'test libcore',
      command = ['art/tools/run-libcore-tests.sh',
                 '--mode=device',
                 '--variant=X%s' % bitness,
                 '--device-dir=/data/local/tmp',
                 '--vm-command=/data/local/tmp/system/bin/art',
                 '--timeout', '120',
                 '--vm-arg', '-Ximage:/data/art-test/core-optimizing.art'],
      haltOnFailure = False,
      description = 'test libcore',
      env = test_env)

  test_jdwp = ShellCommand(
      name = 'test jdwp',
      command = ['art/tools/run-jdwp-tests.sh',
                 '--mode=device',
                 '--variant=X%s' % bitness],
      haltOnFailure = False,
      description = 'test jdwp',
      env = test_env)

  setup_device = ShellCommand(
      name = 'setup device',
      command = ['art/tools/setup-buildbot-device.sh'],
      haltOnFailure = False,
      description = 'setup device',
      env = env)

  cleanup = ShellCommand(
      name = 'device cleanup',
      command = ['adb', 'shell', 'rm', '-rf',
                 '/data/local/tmp', '/data/art-test', '/data/nativetest'],
      haltOnFailure = False,
      description = 'device cleanup',
      env = env)

  dump_log = ShellCommand(
      name = 'adb logcat',
      command = ['adb', 'logcat', '-d', '-v', 'time'],
      haltOnFailure = False,
      description = 'adb logcat',
      env = env)

  clear_log = ShellCommand(
      name = 'adb clear log',
      command = ['adb', 'logcat', '-c'],
      haltOnFailure = False,
      description = 'adb clear log',
      env = env)

  factory.addStep(checkout)
  factory.addStep(maybe_clobber)
  factory.addStep(build)
  factory.addStep(setup_device)
  factory.addStep(sync)
  tests = [test_gtest, test_quick, test_optimizing, test_interpreter,
           test_libcore, test_jdwp]

  for test in tests:
    factory.addStep(test)
    factory.addStep(dump_log)
    factory.addStep(clear_log)

  factory.addStep(cleanup)

def setup_aosp_builder(factory):
  factory.addStep(full_checkout)
  factory.addStep(maybe_clobber)

  builds = ['x86', 'x86_64', 'arm', 'arm64']
  for build in builds:
    factory.addStep(ShellCommand(
      name = 'Clean oat %s' % build,
      command = ['make', '-j8', 'clean-oat-host'],
      haltOnFailure = False,
      description = 'clean oat %s' % build,
      env = { 'ART_USE_OPTIMIZING_COMPILER': 'true',
              'TARGET_PRODUCT': 'aosp_%s' % build,
              'TARGET_BUILD_VARIANT': 'eng',
              'TARGET_BUILD_TYPE': 'release'}))

    factory.addStep(ShellCommand(
      name = 'build %s' % build,
      command = ['make', '-j8'],
      haltOnFailure = False,
      description = 'build %s' % build,
      env = { 'ART_USE_OPTIMIZING_COMPILER': 'true',
              'TARGET_PRODUCT': 'aosp_%s' % build,
              'TARGET_BUILD_VARIANT': 'eng',
              'TARGET_BUILD_TYPE': 'release'}))


art_host_x86_build = BuildFactory()
setup_host_x86(art_host_x86_build, name = 'host-x86', x86_64=False)


art_host_x86_64_build = BuildFactory()
setup_host_x86(art_host_x86_64_build, name = 'host-x86_64', x86_64=True)


art_hammerhead_build_ndebug = BuildFactory()
setup_target(factory = art_hammerhead_build_ndebug,
             name = 'hammerhead-ndebug',
             serial = '03a79ae90ae5889b',
             debug = False,
             device = 'hammerhead')

art_hammerhead_build_debug = BuildFactory()
setup_target(factory = art_hammerhead_build_debug,
             name = 'hammerhead-debug',
             serial = '06ad9349003bb7b1',
             debug = True,
             device = 'hammerhead')

art_armv8_build_ndebug = BuildFactory()
setup_target(factory = art_armv8_build_ndebug,
             name = 'armv8-ndebug',
             serial = 'HT4CTJT03670',
             debug = False,
             device = 'volantis')

art_armv8_build_debug = BuildFactory()
setup_target(factory = art_armv8_build_debug,
             name = 'armv8-debug',
             serial = 'HT49CJT00070',
             debug = True,
             device = 'volantis')

art_fugu_build_ndebug = BuildFactory()
setup_target(factory = art_fugu_build_ndebug,
             name = 'fugu-ndebug',
             serial = '8C4CC68F',
             debug = False,
             device = 'fugu')


art_fugu_build_debug = BuildFactory()
setup_target(factory = art_fugu_build_debug,
             name = 'fugu-debug',
             serial = '0FF57BB6',
             debug = True,
             device = 'fugu')

aosp_builder = BuildFactory()
setup_aosp_builder(aosp_builder)


# Builders setup.
c['builders']= [ {'name' : 'host-x86',
                  'category': '1aosp-host-x86',
                  'factory' : art_host_x86_build,
                  'auto_reboot': False },

                 {'name' : 'host-x86_64',
                  'category': '1aosp-host-x86_64',
                  'factory' : art_host_x86_64_build,
                  'auto_reboot': False },

                 {'name' : 'hammerhead-ndebug',
                  'category': '1aosp-target-armv7',
                  'factory' : art_hammerhead_build_ndebug,
                  'auto_reboot': False },

                 {'name' : 'hammerhead-debug',
                  'category': '1aosp-target-armv7',
                  'factory' : art_hammerhead_build_debug,
                  'auto_reboot': False },

                 {'name' : 'armv8-ndebug',
                  'category': '1aosp-target-armv8',
                  'factory' : art_armv8_build_ndebug,
                  'auto_reboot': False },

                 {'name' : 'armv8-debug',
                  'category': '1aosp-target-armv8',
                  'factory' : art_armv8_build_debug,
                  'auto_reboot': False },

                 {'name' : 'fugu-ndebug',
                  'category': '1aosp-target-fugu',
                  'factory' : art_fugu_build_ndebug,
                  'auto_reboot': False },

                 {'name' : 'fugu-debug',
                  'category': '1aosp-target-fugu',
                  'factory' : art_fugu_build_debug,
                  'auto_reboot': False },

                 {'name' : 'aosp-builder',
                  'category': '2aosp-builder',
                  'factory' : aosp_builder,
                  'auto_reboot': False },
               ]


repopoller = RepoPoller(
    repo_url = 'https://android.googlesource.com/platform',
    manifest = 'manifest',
    repo_branches = ['master-art'],
    pollInterval = 300,
    revlinktmpl = 'https://android.googlesource.com/platform/%s/+/%s')
c['change_source'] = repopoller


# Scheduler setup
trunkchanged = SingleBranchScheduler(
    name = 'trunkchanged',
    change_filter = filter.ChangeFilter(branch = 'master-art'),
    treeStableTimer = 0,
    builderNames = ['host-x86',
                    'host-x86_64',
                    'hammerhead-ndebug',
                    'hammerhead-debug',
                    'armv8-ndebug',
                    'armv8-debug',
                    'fugu-ndebug',
                    'fugu-debug',
                    'aosp-builder'])
c['schedulers'] = [ trunkchanged ]


# Slave setup
slaves = slaves_list.SlavesList('slaves.cfg', 'ART')
for builder in c['builders']:
  # Associate the slaves to the builders. The configuration is in slaves.cfg.
  builder['slavenames'] = slaves.GetSlavesName(builder=builder['name'])


c['slaves'] = master_utils.AutoSetupSlaves(
    c['builders'],
    config.Master.GetBotPassword(),
    max_builds=2)


# Adds common status and tools to this master.
master_utils.AutoSetupMaster(c, ActiveMaster,
    public_html='../master.chromium/public_html',
    templates=['./templates', '../master.chromium/templates'],
    order_console_by_time=True)


if ActiveMaster.is_production_host:
  c['status'].append(MailNotifier(
      fromaddr=ActiveMaster.from_address,
      sendToInterestedUsers=False,
      relayhost=config.Master.smtp,
      mode='problem',
      extraRecipients=['ngeoffray@google.com',
                       'agampe@google.com',
                       'dalvik-team+chromium-buildbot@google.com']))

####### PROJECT IDENTITY

c['projectName'] = ActiveMaster.project_name
c['projectURL'] = config.Master.project_url
c['buildbotURL'] = ActiveMaster.buildbot_url
